/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_orm.htm
 *
 * Zhiqim ORM is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.orm.dbo;

import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;

import org.zhiqim.kernel.annotation.AnAlias;
import org.zhiqim.kernel.annotation.AnNew;
import org.zhiqim.kernel.model.maps.HashMapSV;
import org.zhiqim.kernel.model.maps.LinkedMapSS;
import org.zhiqim.kernel.model.maps.MapSO;
import org.zhiqim.kernel.util.Arrays;
import org.zhiqim.kernel.util.Lists;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Validates;
import org.zhiqim.orm.ORMException;
import org.zhiqim.orm.ZTable;
import org.zhiqim.orm.dbo.defined._Table;
import org.zhiqim.orm.dbo.defined._ViewTableName;

/**
 * 对象查询器，支持查询字段、条件和排序条件
 *
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
@AnAlias("Selector")
@AnNew
public class Selector extends Conditioner<Selector>
{
    private static final long serialVersionUID = 1L;
    private static final String FIELD_FORMAT = " ,_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
    
    private String fields = null;
    private String groupby = null;
    private LinkedMapSS orderbyMap = null;
    private HashMapSV<Selector> joinMap = null;
    
    /** 构造空查询器，构造后增加参数 */
    public Selector()
    {
    }
    
    /** 构造指定字段串的查询器 */
    public Selector(String fields)
    {
        addFields(fields);
    }
    
    /** 构造指定一个相等条件的查询器 */
    public Selector(String field, Object value)
    {
        addMust(field, value);
    }
    
    /** 构造指定字段串和一个相等条件的查询器 */
    public Selector(String fields, String field, Object value)
    {
        addFields(fields);
        addMust(field, value);
    }
    
    /** 增加一个条件 */
    public Selector(Condition condition)
    {
        addCondition(condition);
    }
    
    /** 增加字段字符串 */
    public Selector addFields(String fields)
    {
        if (!Validates.isScope(fields, FIELD_FORMAT))
            return this;
        
        //去除前后空格和逗号
        fields = fields.trim();
        fields = Strings.trimLeft(fields, ",");
        fields = Strings.trimRight(fields, ",");
        
        if (Validates.isEmptyBlank(fields))
            return this;
        
        if (this.fields != null)
            fields = this.fields + "," + fields;

        //去除相同的字段
        String[] fieldArr = Arrays.toStringArray(fields);
        this.fields = Arrays.toFilterSameStr(fieldArr);
        return this;
    }
    
    /** 清除字段字符串 */
    public Selector clearFields()
    {
        this.fields = null;
        return this;
    }
    
    /**
     * 增加适配信息
     * 
     * @param key   适配建
     * @param value 适配值
     * @return      返回本对象
     */
    public Selector addReplace(String key, String value)
    {
        addReplaceInner(key, value);
        return this;
    }
    
    /** 增加分组字段字符串 */
    public Selector addGroupbyFields(String fields)
    {
        if (!Validates.isScope(fields, FIELD_FORMAT))
            return this;
        
        //去除前后空格和逗号
        fields = fields.trim();
        fields = Strings.trimLeft(fields, ",");
        fields = Strings.trimRight(fields, ",");
        
        if (Validates.isEmptyBlank(fields))
            return this;
        
        if (this.groupby != null)
            fields = this.groupby + "," + fields;

        //去除相同的字段
        String[] fieldArr = Arrays.toStringArray(fields);
        this.groupby = Arrays.toFilterSameStr(fieldArr);
        return this;
    }
    
    /** 清除分组字段字符串 */
    public Selector clearGroupbyFields()
    {
        this.groupby = null;
        return this;
    }
    
    /** 增加左右连别名查询器 */
    public Selector addJoin(String alias, Selector selector)
    {
        if (joinMap == null)
            joinMap = new HashMapSV<>();
            
        joinMap.put(alias, selector);
        return this;
    }
    
    /** 增加左右连类查询器 */
    public Selector addJoin(Class<?> clazz, Selector selector)
    {
        if (joinMap == null)
            joinMap = new HashMapSV<>();
            
        joinMap.put(clazz.getName(), selector);
        return this;
    }
    
    /**
     * 组装Where条件的SQL语句
     * 
     * @param dbo               _Dbo/_Table/_View三种的一种
     * @param tableAlias        表别名
     * @param tableName         表真实名称
     * @param tableFull         表真实名后连表别名
     * @param paramMap          参数表
     * @return                  SQL字符串
     * @throws ORMException     异常
     */
    public String getJoinWhereSQL(ZTable zTable, _ViewTableName table, MapSO paramMap) throws ORMException
    {
        if (joinMap == null)
            return table.getTableFull();
        
        String tableAlias = table.getTableAlias(), tableName = table.getTableName(), tableFull = table.getTableFull();
        
        //先判断是否从别名中获取到，没有再找类名
        _Table _table = zTable.getTableByTableName(tableName);
        Selector selector = joinMap.get(tableAlias);
        if (selector == null)
            selector = joinMap.get(_table.getName());
        
        if (selector == null)
            return tableFull;
        
        List<Condition> conditionList = selector.getConditionList();
        if (conditionList == null || conditionList.isEmpty())
            return tableFull;
        
        //如果相等的连接条件，后面加and即可,否则先加where,再接and
        StringBuilder whereSQL = new StringBuilder();
        //第一个
        whereSQL.append("(select ").append(selector.getFieldSQL(_table))
                .append(" from ").append(tableName)
                .append(" where ");
        whereSQL.append(conditionList.get(0).toSql(_table, paramMap));
        //后面的
        for (int i=1;i<conditionList.size();i++)
        {
            whereSQL.append(" and ").append(conditionList.get(i).toSql(_table, paramMap));
        }
        
        whereSQL.append(") as ").append(tableAlias);
        return whereSQL.toString();
    }
    
    /******************************************************/
    //排序管理
    /******************************************************/
    
    /** 判断是否有排序 */
    public boolean hasOrderby()
    {
        return orderbyMap != null && !orderbyMap.isEmpty();
    }
    
    /** 获取排序列表 */
    public LinkedMapSS getOrderby()
    {
        return orderbyMap;
    }
    
    /**
     * 增加顺序排序方式
     * 
     * @param fields 排序属性
     */
    public Selector addOrderbyAsc(String fields)
    {
        if (orderbyMap == null)
            orderbyMap = new LinkedMapSS(2);

        String[] fieldArr = Arrays.toStringArray(fields);
        for (String field : fieldArr){
            orderbyMap.put(field, ASC);
        }
        return this;
    }
    
    /**
     * 增加倒序排序方式
     * 
     * @param field 排序属性
     */
    public Selector addOrderbyDesc(String fields)
    {
        if (orderbyMap == null)
            orderbyMap = new LinkedMapSS(2);

        String[] fieldArr = Arrays.toStringArray(fields);
        for (String field : fieldArr){
            orderbyMap.put(field, DESC);
        }
        return this;
    }
    
    /** 获取字段字符串 */
    public String getFields()
    {
        return fields;
    }
    
    /** 获取字段SQL */
    public String getFieldSQL(Dbo dbo) throws ORMException
    {
        if (Validates.isEmptyBlank(fields))
            return "*";
        
        StringBuilder strb = new StringBuilder();
        String[] fieldArr = Arrays.toStringArray(fields);
        for (String field : fieldArr)
        {
            String column = dbo.getColumn(field);
            if (column == null)
                continue;
            
            strb.append(column).append(",");
        }
        if (strb.length() > 0){
            strb.setLength(strb.length()-1);
        }
        return strb.toString();
    }
    
    /** 检查排序 */
    public String getOrderbySQL(Dbo dbo) throws ORMException
    {
        if (orderbyMap == null || orderbyMap.isEmpty())
            return _EMPTY_;
        
        StringBuilder strb = new StringBuilder(" order by");
        for (Entry<String, String> entry : orderbyMap.entrySet())
        {
            String field = entry.getKey();
            if (!dbo.hasField(field))
                throw new ORMException("映射配置异常,在配置["+dbo.getName()+"]中未找到["+field+"]字段");
            
            String column = dbo.getColumn(field);
            strb.append(" ").append(column).append(" ").append(entry.getValue()).append(",");
        }
        strb.setLength(strb.length()-1);
        return strb.toString();
    }
    
    /** 检查排序并反转，用于分页时倒序的倒序 */
    public String getOrderbyReverseSQL(Dbo dbo) throws ORMException
    {
        if (orderbyMap == null || orderbyMap.isEmpty())
            return _EMPTY_;

        //先转换成列表
        ArrayList<String[]> orderbyList = new ArrayList<String[]>();
        for (Entry<String, String> entry : orderbyMap.entrySet())
        {
            String field = entry.getKey();
            if (!dbo.hasField(field))
                throw new ORMException("映射配置异常,在配置["+dbo.getName()+"]中未找到["+field+"]字段");
            
            orderbyList.add(new String[]{field, (ASC.equals(entry.getValue()))?DESC:ASC});
        }
        //再作反转
        orderbyList = Lists.reverse(orderbyList);
        
        //生成字符串
        StringBuilder strb = new StringBuilder(" order by");
        for (String[] orderbyArr : orderbyList)
        {
            String field = orderbyArr[0];
            String value = orderbyArr[1];
            String column = dbo.getColumn(field);
            
            strb.append(" ").append(column).append(" ").append(value).append(",");
        }
        
        strb.setLength(strb.length()-1);
        return strb.toString();
    }

    public String getGroupby()
    {
        return groupby;
    }
    
    public String getGroupbyFieldsSQL(Dbo dbo) throws ORMException
    {
        if (Validates.isEmptyBlank(groupby))
            return _EMPTY_;
        
        StringBuilder strb = new StringBuilder();
        String[] fieldArr = Arrays.toStringArray(groupby);
        for (String field : fieldArr)
        {
            String column = dbo.getColumn(field);
            if (column == null)
                throw new ORMException("映射配置异常,在配置["+dbo.getName()+"]中未找到["+field+"]属性");
            
            strb.append(" ").append(column).append(",");
        }
        strb.setLength(strb.length()-1);
        return strb.toString();
    }
    
    /** 获取分组SQL */
    public String getGroupbySQL(Dbo dbo) throws ORMException
    {
        if (Validates.isEmptyBlank(groupby))
            return _EMPTY_;
        
        StringBuilder strb = new StringBuilder(" group by");
        String[] fieldArr = Arrays.toStringArray(groupby);
        for (String field : fieldArr)
        {
            String column = dbo.getColumn(field);
            if (column == null)
                throw new ORMException("映射配置异常,在配置["+dbo.getName()+"]中未找到["+field+"]属性");
            
            strb.append(" ").append(column).append(",");
        }
        strb.setLength(strb.length()-1);
        return strb.toString();
    }
}
